// Package main cert-deploy-nginx 主程序
package main

import (
    "bufio"
    "context"
    "crypto/sha256"
    "crypto/x509"
    "encoding/hex"
    "encoding/json"
    "encoding/pem"
    "fmt"
    "io"
    "log/slog"
    "os"
    "path/filepath"
    "strings"
    "time"

    "github.com/cnssl/cert-deploy/internal/backup"
    "github.com/cnssl/cert-deploy/internal/config"
    "github.com/cnssl/cert-deploy/internal/daemon"
    "github.com/cnssl/cert-deploy/internal/deployer"
    "github.com/cnssl/cert-deploy/internal/csr"
    "github.com/cnssl/cert-deploy/internal/fetcher"
    "github.com/cnssl/cert-deploy/internal/scanner"
    "github.com/cnssl/cert-deploy/internal/util"
    "github.com/cnssl/cert-deploy/internal/validator"
    "github.com/spf13/cobra"
)

var (
    // 全局变量
    cfgManager *config.Manager
    logger     *slog.Logger

	// 命令行标志
	siteName string
    referID  string
    apiURL   string
    force    bool

    // 扫描配置
    nginxDirs string // 逗号分隔的自定义 Nginx 配置目录

    // 私钥/CSR 生成参数
    keyType  string // rsa|ecdsa
    keySize  int    // 2048|4096 (RSA)
    keyCurve string // prime256v1|secp384r1|secp521r1 (ECDSA)
    csrOrg   string
    csrC     string
    csrST    string
    csrL     string
    csrEmail string
    valMethod string
    webroot string
)

// 通过 ldflags 注入版本号，例如：-ldflags "-X main.version=v2.0.1"
var version = "dev"

func main() {
	// 获取可执行文件路径
	execPath, err := os.Executable()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get executable path: %v\n", err)
		os.Exit(1)
	}

	// 初始化配置管理器
	cfgManager, err = config.NewManager(execPath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to initialize config manager: %v\n", err)
		os.Exit(1)
	}

	// 初始化日志
	logger = setupLogger()

	// 创建根命令
	rootCmd := &cobra.Command{
		Use:   "cert-deploy-nginx",
		Short: "Automatic certificate deployment tool for Nginx",
		Long:  "A lightweight tool to automatically deploy certificates from API to Nginx servers",
	}

	// 添加子命令
	rootCmd.AddCommand(scanCmd())
	rootCmd.AddCommand(initCmd())
	rootCmd.AddCommand(deployCmd())
	rootCmd.AddCommand(statusCmd())
	rootCmd.AddCommand(checkCmd())
    rootCmd.AddCommand(daemonCmd())
    rootCmd.AddCommand(versionCmd())
    rootCmd.AddCommand(configCmd())
    rootCmd.AddCommand(autoCmd())

	// 执行命令
	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}

// initCmd 初始化站点配置命令
func initCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "init",
		Short: "Initialize site configuration",
		Long:  "Create a new site configuration file with the specified settings",
		RunE:  runInit,
	}

	cmd.Flags().StringVar(&siteName, "site", "", "Site name (required)")
	cmd.Flags().StringVar(&referID, "refer-id", "", "API refer ID (required)")
	cmd.Flags().StringVar(&apiURL, "api-url", "", "API URL (required)")
    // Key/CSR flags
    cmd.Flags().StringVar(&keyType, "key-type", "rsa", "Key type: rsa|ecdsa (default rsa)")
    cmd.Flags().IntVar(&keySize, "key-size", 2048, "RSA key size: 2048|4096 (default 2048)")
    cmd.Flags().StringVar(&keyCurve, "key-curve", "prime256v1", "ECDSA curve: prime256v1|secp384r1|secp521r1")
    cmd.Flags().StringVar(&csrOrg, "csr-org", "", "CSR Organization (O)")
    cmd.Flags().StringVar(&csrC, "csr-c", "", "CSR Country (C)")
    cmd.Flags().StringVar(&csrST, "csr-st", "", "CSR State/Province (ST)")
    cmd.Flags().StringVar(&csrL, "csr-l", "", "CSR Locality/City (L)")
    cmd.Flags().StringVar(&csrEmail, "csr-email", "", "CSR Email")
    cmd.Flags().StringVar(&valMethod, "validation-method", "", "Validation method: txt|file|admin|administrator|postmaster|webmaster|hostmaster")
    cmd.Flags().StringVar(&webroot, "webroot", "", "Webroot for file validation (optional)")

    cmd.MarkFlagRequired("site")
	cmd.MarkFlagRequired("refer-id")
	cmd.MarkFlagRequired("api-url")

	return cmd
}

// deployCmd 部署证书命令
func deployCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "deploy",
		Short: "Deploy certificate to site",
		Long:  "Fetch certificate from API and deploy to the specified site",
		RunE:  runDeploy,
	}

	cmd.Flags().StringVar(&siteName, "site", "", "Site name (required)")
	cmd.Flags().BoolVar(&force, "force", false, "Force deployment even if certificate is not expiring")
	cmd.MarkFlagRequired("site")

	return cmd
}

// statusCmd 查看站点状态命令
func statusCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "status",
		Short: "Show status of all sites",
		Long:  "Display the status of all configured sites",
		RunE:  runStatus,
	}

	return cmd
}

// checkCmd 检查证书状态命令
func checkCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "check",
		Short: "Check certificate expiration",
		Long:  "Check if certificates need renewal",
		RunE:  runCheck,
	}

	cmd.Flags().StringVar(&siteName, "site", "", "Site name (check specific site)")

	return cmd
}

// scanCmd 扫描配置命令
func scanCmd() *cobra.Command {
    cmd := &cobra.Command{
        Use:   "scan",
        Short: "Scan Nginx configuration",
        Long:  "Scan Nginx configuration files and discover SSL sites",
        RunE:  runScan,
    }

    cmd.Flags().StringVar(&nginxDirs, "nginx-dirs", "", "Comma-separated Nginx config directories (override defaults)")

    return cmd
}

// daemonCmd 守护进程命令
func daemonCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "daemon",
		Short: "Run in daemon mode",
		Long:  "Run in background and automatically check/deploy certificates",
		RunE:  runDaemon,
	}

	return cmd
}

// versionCmd 版本命令
func versionCmd() *cobra.Command {
    cmd := &cobra.Command{
        Use:   "version",
        Short: "Show version information",
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Printf("cert-deploy-nginx %s\n", version)
        },
    }

    return cmd
}

// config 命令
func configCmd() *cobra.Command {
    cmd := &cobra.Command{Use: "config", Short: "Manage site config"}
    cmd.AddCommand(configSetCmd())
    return cmd
}

func configSetCmd() *cobra.Command {
    var site string
    var setKeyType string
    var setKeySize int
    var setKeyCurve string
    var setOrg, setC, setST, setL, setEmail string
    c := &cobra.Command{
        Use:   "set",
        Short: "Set key/csr options for a site",
        RunE: func(cmd *cobra.Command, args []string) error {
            if site == "" { return fmt.Errorf("--site is required") }
            sc, err := cfgManager.LoadSite(site)
            if err != nil { return fmt.Errorf("load site failed: %w", err) }
            if setKeyType != "" { sc.Key.Type = setKeyType }
            if setKeySize != 0 { sc.Key.Size = setKeySize }
            if setKeyCurve != "" { sc.Key.Curve = setKeyCurve }
            if setOrg != "" { sc.CSR.Organization = setOrg }
            if setC != "" { sc.CSR.Country = setC }
            if setST != "" { sc.CSR.State = setST }
            if setL != "" { sc.CSR.Locality = setL }
            if setEmail != "" { sc.CSR.Email = setEmail }
            if err := cfgManager.SaveSite(sc); err != nil { return fmt.Errorf("save site failed: %w", err) }
            fmt.Println("✓ Config updated for", site)
            return nil
        },
    }
    c.Flags().StringVar(&site, "site", "", "Site name (required)")
    c.Flags().StringVar(&setKeyType, "key-type", "", "rsa|ecdsa")
    c.Flags().IntVar(&setKeySize, "key-size", 0, "RSA key size: 2048|4096")
    c.Flags().StringVar(&setKeyCurve, "key-curve", "", "ECDSA curve")
    c.Flags().StringVar(&setOrg, "csr-org", "", "CSR Organization (O)")
    c.Flags().StringVar(&setC, "csr-c", "", "CSR Country (C)")
    c.Flags().StringVar(&setST, "csr-st", "", "CSR State/Province (ST)")
    c.Flags().StringVar(&setL, "csr-l", "", "CSR Locality/City (L)")
    c.Flags().StringVar(&setEmail, "csr-email", "", "CSR Email")
    return c
}
// autoCmd 一键向导（扫描 -> 映射 refer_id -> 生成配置 -> 可选部署/守护）
func autoCmd() *cobra.Command {
    cmd := &cobra.Command{
        Use:   "auto",
        Short: "Interactive setup wizard",
        RunE:  runAuto,
    }
    cmd.Flags().StringVar(&nginxDirs, "nginx-dirs", "", "Comma-separated Nginx config directories (override defaults)")
    cmd.Flags().StringVar(&apiURL, "api-url", "", "API URL (required if not found in configs)")
    // allow specifying defaults for new sites
    cmd.Flags().StringVar(&keyType, "key-type", "rsa", "Key type: rsa|ecdsa (default rsa)")
    cmd.Flags().IntVar(&keySize, "key-size", 2048, "RSA key size: 2048|4096 (default 2048)")
    cmd.Flags().StringVar(&keyCurve, "key-curve", "prime256v1", "ECDSA curve: prime256v1|secp384r1|secp521r1")
    cmd.Flags().StringVar(&csrOrg, "csr-org", "", "CSR Organization (O)")
    cmd.Flags().StringVar(&csrC, "csr-c", "", "CSR Country (C)")
    cmd.Flags().StringVar(&csrST, "csr-st", "", "CSR State/Province (ST)")
    cmd.Flags().StringVar(&csrL, "csr-l", "", "CSR Locality/City (L)")
    cmd.Flags().StringVar(&csrEmail, "csr-email", "", "CSR Email")
    cmd.Flags().StringVar(&valMethod, "validation-method", "", "Validation method: txt|file|admin|administrator|postmaster|webmaster|hostmaster")
    cmd.Flags().StringVar(&webroot, "webroot", "", "Webroot for file validation (optional)")
    return cmd
}

// runAuto 执行一键向导
func runAuto(cmd *cobra.Command, args []string) error {
    fmt.Println("→ Welcome to cert-deploy-nginx interactive setup")

    // 1) 扫描 Nginx 站点
    var s *scanner.NginxScanner
    if strings.TrimSpace(nginxDirs) != "" {
        var dirs []string
        for _, p := range strings.Split(nginxDirs, ",") {
            p = strings.TrimSpace(p)
            if p != "" {
                dirs = append(dirs, p)
            }
        }
        s = scanner.NewNginxScannerWithDirs(dirs)
    } else {
        s = scanner.NewNginxScanner()
    }
    sites, err := s.Scan()
    if err != nil {
        return fmt.Errorf("scan failed: %w", err)
    }
    if len(sites) == 0 {
        fmt.Println("✗ No SSL sites found. You can still initialize configs.")
    } else {
        fmt.Printf("✓ Found %d SSL site(s)\n", len(sites))
    }

    // 2) 收集 refer_id（多行输入，空行结束）或复用现有
    referIDs := readReferIDs()
    if len(referIDs) == 0 {
        // 尝试从现有配置复用
        old, _ := cfgManager.ListSites()
        for _, sc := range old {
            if sc.API.ReferID != "" {
                referIDs = append(referIDs, sc.API.ReferID)
            }
        }
        if len(referIDs) > 0 {
            if !promptYesNo(fmt.Sprintf("Use %d refer_id(s) discovered from existing configs?", len(referIDs)), true) {
                referIDs = nil
            }
        }
    }
    if len(referIDs) == 0 {
        return fmt.Errorf("no refer_id provided. Please provide at least one")
    }

    // 3) API URL（若未指定，从旧配置获取首个；否则询问）
    api := strings.TrimSpace(apiURL)
    if api == "" {
        if old, _ := cfgManager.ListSites(); len(old) > 0 && old[0].API.URL != "" {
            api = old[0].API.URL
        }
    }
    if api == "" {
        fmt.Print("Enter API URL: ")
        api = readLine()
        api = strings.TrimSpace(api)
        if api == "" {
            return fmt.Errorf("API URL is required")
        }
    }

    // 4) 若已存在 sites 配置，校验并可选择备份重建
    existing, _ := cfgManager.ListSites()
    if len(existing) > 0 {
        modified := false
        for _, sc := range existing {
            if ok, err := validateConfigHash(sc); !ok {
                modified = true
                logger.Warn("site config modified", "site", sc.SiteName, "error", err)
            }
        }
        fmt.Printf("Found %d existing site config(s).\n", len(existing))
        if modified {
            fmt.Println("Some configs seem modified manually.")
            // 若未允许使用被修改的配置，则直接拒绝继续
            if strings.ToLower(os.Getenv("ALLOW_MODIFIED_CONFIG")) != "true" {
                return fmt.Errorf("config validation failed: modified configs detected. Set ALLOW_MODIFIED_CONFIG=true to bypass")
            }
        }
        if !promptYesNo("Use existing configs? (Y=keep, N=backup and regenerate)", true) {
            if err := backupAndResetSites(); err != nil {
                return err
            }
            existing = nil
        }
    }

    // 5) 生成站点配置（按扫描到的站点生成；若 refer_id 不足则部分站点禁用）
    idx := 0
    for _, si := range sites {
        rid := ""
        if idx < len(referIDs) {
            rid = referIDs[idx]
        }
        idx++

        siteCfg := &config.SiteConfig{
            Version:    "1.0",
            SiteName:   si.Name,
            Enabled:    rid != "",
            ServerType: "nginx",
            API: config.APIConfig{URL: api, ReferID: rid},
            Domains: si.Domains,
            Paths: config.PathsConfig{
                Certificate: si.CertPath, // 复用现有路径
                PrivateKey:  si.KeyPath,
                ConfigFile:  si.ConfigFile,
                Webroot:     webroot,
            },
            Reload: config.ReloadConfig{TestCommand: "nginx -t", ReloadCommand: "systemctl reload nginx"},
            Schedule:   config.ScheduleConfig{CheckIntervalHours: 12, RenewBeforeDays: 15},
            Validation: config.ValidationConfig{VerifyDomain: true, TestHTTPS: false, TestURL: fmt.Sprintf("https://%s", si.Name), Method: valMethod},
            Backup:     config.BackupConfig{Enabled: true, KeepVersions: 3},
            Metadata:   config.MetadataConfig{CreatedAt: time.Now(), AutoScanned: true},
            Key:        config.KeyConfig{Type: keyType, Size: keySize, Curve: keyCurve},
            CSR:        config.CSRConfig{Organization: csrOrg, Country: csrC, State: csrST, Locality: csrL, Email: csrEmail},
        }

        // 写入并计算哈希
        if err := cfgManager.SaveSite(siteCfg); err != nil {
            return fmt.Errorf("save site failed: %w", err)
        }
        if err := setSiteConfigHash(siteCfg); err != nil {
            logger.Warn("failed to set config hash", "site", siteCfg.SiteName, "error", err)
        }

        // 若 refer_id 已存在，立即发起签发（生成CSR并POST），并根据 file 验证落地文件
        if rid != "" {
            issueErr := startIssuance(siteCfg)
            if issueErr != nil {
                logger.Warn("issuance start failed", "site", siteCfg.SiteName, "error", issueErr)
            }
        }
    }

    // 6) 写入 SSL 模板（如不存在）
    if err := ensureTemplates(); err != nil {
        logger.Warn("failed to write templates", "error", err)
    }

    // 7) 输出总结与后续建议
    fmt.Println("\n✓ Setup completed.")
    fmt.Println("- Edit configs under:", cfgManager.GetSitesDir())
    fmt.Println("- Start daemon: cert-deploy-nginx daemon")
    return nil
}

// readReferIDs 从 stdin 读取多行 refer_id（空行结束）
func readReferIDs() []string {
    fmt.Println("Enter refer_id (one per line), blank line to finish:")
    var ids []string
    for {
        line := readLine()
        if strings.TrimSpace(line) == "" {
            break
        }
        ids = append(ids, strings.TrimSpace(line))
    }
    return ids
}

// readLine 读取一行输入
func readLine() string {
    // 使用缓冲读取，避免空格截断；限制长度，提升健壮性
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("> ")
    line, err := reader.ReadString('\n')
    if err != nil && err != io.EOF {
        logger.Error("failed to read input", "error", err)
        return ""
    }
    line = strings.TrimSpace(line)
    const maxInputLength = 1024
    if len(line) > maxInputLength {
        logger.Warn("input too long, truncated", "length", len(line))
        line = line[:maxInputLength]
    }
    return line
}

// promptYesNo 带默认值的 Y/N 提示
func promptYesNo(prompt string, defYes bool) bool {
    def := "Y"
    if !defYes { def = "N" }
    fmt.Printf("%s [%s]: ", prompt, def)
    in := strings.TrimSpace(strings.ToLower(readLine()))
    if in == "" { return defYes }
    return in == "y" || in == "yes"
}

// validateConfigHash 校验配置是否被手工修改（对比 Metadata.ConfigHash）
func validateConfigHash(sc *config.SiteConfig) (bool, error) {
    // 构造一个副本，清空 ConfigHash 再计算
    cp := *sc
    cp.Metadata.ConfigHash = ""
    data, err := json.Marshal(cp)
    if err != nil { return false, fmt.Errorf("failed to marshal config: %w", err) }
    got := utilHash(data)
    if sc.Metadata.ConfigHash == "" {
        return false, fmt.Errorf("config hash is empty")
    }
    if got != sc.Metadata.ConfigHash {
        return false, fmt.Errorf("config hash mismatch")
    }
    return true, nil
}

// setSiteConfigHash 写入 Metadata.ConfigHash
func setSiteConfigHash(sc *config.SiteConfig) error {
    cp := *sc
    cp.Metadata.ConfigHash = ""
    data, err := json.Marshal(cp)
    if err != nil { return err }
    sc.Metadata.ConfigHash = utilHash(data)
    return cfgManager.SaveSite(sc)
}

func utilHash(b []byte) string {
    return utilHashSHA256(b)
}

func utilHashSHA256(b []byte) string {
    h := sha256Sum(b)
    return hexEncode(h)
}

// 轻量封装（避免在 main 中直接引入 util 包命名冲突）
func sha256Sum(b []byte) [32]byte { return sha256.Sum256(b) }
func hexEncode(b [32]byte) string { return hex.EncodeToString(b[:]) }

func backupAndResetSites() error {
    src := cfgManager.GetSitesDir()
    ts := time.Now().Format("20060102-150405")
    dst := filepath.Join(cfgManager.GetWorkDir(), "sites.bak-"+ts)
    if err := os.Rename(src, dst); err != nil {
        return fmt.Errorf("backup sites failed: %w", err)
    }
    return os.MkdirAll(src, 0755)
}

func ensureTemplates() error {
    tplDir := filepath.Join(cfgManager.GetWorkDir(), "templates")
    if err := os.MkdirAll(tplDir, 0755); err != nil { return err }
    nginxTpl := `# Nginx SSL template
server {
    listen 443 ssl;
    server_name {{ .ServerName }};
    # Copy common settings from your port 80 server block (root/proxy/etc.)
    ssl_certificate {{ .Certificate }};
    ssl_certificate_key {{ .PrivateKey }};
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
}
`
    return os.WriteFile(filepath.Join(tplDir, "nginx_ssl.tmpl"), []byte(nginxTpl), 0644)
}

// startIssuance 为站点生成本地 Key/CSR 并发起更新；处理 file 验证
func startIssuance(sc *config.SiteConfig) error {
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    f := fetcher.New(30 * time.Second)

    // 获取 common_name（若服务端提供）
    info, _ := f.Info(ctx, sc.API.URL, sc.API.ReferID)
    cn := sc.CSR.CommonName
    if cn == "" {
        if info != nil && info.CommonName != "" { cn = info.CommonName } else if len(sc.Domains) > 0 { cn = sc.Domains[0] } else { cn = sc.SiteName }
    }

    // 生成 Key/CSR
    keyPEM, csrPEM, _, err := csr.GenerateKeyAndCSR(
        csr.KeyOptions{Type: sc.Key.Type, Size: sc.Key.Size, Curve: sc.Key.Curve},
        csr.CSROptions{CommonName: cn, Organization: sc.CSR.Organization, Country: sc.CSR.Country, State: sc.CSR.State, Locality: sc.CSR.Locality, Email: sc.CSR.Email},
    )
    if err != nil { return err }
    if err := os.MkdirAll(filepath.Dir(sc.Paths.PrivateKey), 0755); err == nil {
        if werr := os.WriteFile(sc.Paths.PrivateKey, []byte(keyPEM), 0600); werr != nil { return werr }
    }

    // POST 启动签发
    logger.Info("start issuance", "site", sc.SiteName, "refer", util.MaskReferID(sc.API.ReferID), "method", sc.Validation.Method)
    resp, err := f.StartOrUpdate(ctx, sc.API.URL, sc.API.ReferID, csrPEM, cn, sc.Validation.Method)
    if err != nil { return err }
    if resp != nil && resp.File != nil && strings.EqualFold(sc.Validation.Method, "file") && sc.Paths.Webroot != "" {
        target := filepath.Join(sc.Paths.Webroot, resp.File.Path)
        if err := os.MkdirAll(filepath.Dir(target), 0755); err == nil {
            _ = os.WriteFile(target, []byte(resp.File.Content), 0644)
        }
    }
    return nil
}

// runInit 执行初始化
func runInit(cmd *cobra.Command, args []string) error {
	logger.Info("initializing site configuration", "site", siteName)

	// 检查站点是否已存在
	if cfgManager.SiteExists(siteName) {
		return fmt.Errorf("site configuration already exists: %s", siteName)
	}

	// 创建默认配置
	siteConfig := &config.SiteConfig{
		Version:    "1.0",
		SiteName:   siteName,
		Enabled:    true,
		ServerType: "nginx",
		API: config.APIConfig{
			URL:     apiURL,
			ReferID: referID,
		},
		Domains: []string{siteName},
            Paths: config.PathsConfig{
                Certificate: fmt.Sprintf("/etc/nginx/ssl/%s/fullchain.pem", siteName),
                PrivateKey:  fmt.Sprintf("/etc/nginx/ssl/%s/key.pem", siteName),
                ConfigFile:  fmt.Sprintf("/etc/nginx/sites-enabled/%s.conf", siteName),
                Webroot:     webroot,
            },
        Reload: config.ReloadConfig{
            TestCommand:   "nginx -t",
            ReloadCommand: "systemctl reload nginx",
        },
        Schedule: config.ScheduleConfig{
            CheckIntervalHours: 12,
            RenewBeforeDays:    15,
        },
        Backup: config.BackupConfig{
            Enabled:      true,
            KeepVersions: 3,
        },
        Metadata: config.MetadataConfig{
            CreatedAt:   time.Now(),
            AutoScanned: false,
        },
        Key: config.KeyConfig{Type: keyType, Size: keySize, Curve: keyCurve},
        CSR: config.CSRConfig{Organization: csrOrg, Country: csrC, State: csrST, Locality: csrL, Email: csrEmail},
        Validation: config.ValidationConfig{
            VerifyDomain: true, TestHTTPS: false, TestURL: fmt.Sprintf("https://%s", siteName), IgnoreDomainMismatch: false, Method: valMethod,
        },
    }

	// 保存配置
	if err := cfgManager.SaveSite(siteConfig); err != nil {
		return fmt.Errorf("failed to save configuration: %w", err)
	}

	logger.Info("site configuration created",
		"site", siteName,
		"config_file", cfgManager.GetSitesDir()+"/"+siteName+".json")

    fmt.Printf("✓ Created configuration for %s\n", siteName)
    fmt.Printf("  Key: type=%s", keyType)
    if keyType == "rsa" { fmt.Printf(", size=%d", keySize) } else { fmt.Printf(", curve=%s", keyCurve) }
    fmt.Printf("\n")
    if csrOrg != "" || csrC != "" || csrST != "" || csrL != "" || csrEmail != "" {
        fmt.Printf("  CSR: O=%s, C=%s, ST=%s, L=%s, Email=%s\n", csrOrg, csrC, csrST, csrL, csrEmail)
    }
    if valMethod != "" { fmt.Printf("  Validation method: %s\n", valMethod) }
    if webroot != "" { fmt.Printf("  Webroot: %s\n", webroot) }
	fmt.Printf("  Config file: %s/%s.json\n", cfgManager.GetSitesDir(), siteName)
	fmt.Printf("\nNext steps:\n")
	fmt.Printf("  1. Edit the configuration file to match your setup\n")
    fmt.Printf("  2. Run 'cert-deploy-nginx deploy --site %s' to deploy certificate\n", siteName)

	return nil
}

// runDeploy 执行部署
func runDeploy(cmd *cobra.Command, args []string) error {
	logger.Info("starting deployment", "site", siteName)

	// 1. 加载站点配置
	siteConfig, err := cfgManager.LoadSite(siteName)
	if err != nil {
		return fmt.Errorf("failed to load site configuration: %w", err)
	}

	if !siteConfig.Enabled {
		logger.Info("site is disabled, skipping", "site", siteName)
		return nil
	}

	// 2. 检查是否需要续期
	if !force && !siteConfig.NeedsRenewal() {
		days := siteConfig.DaysUntilExpiry()
		logger.Info("certificate does not need renewal",
			"site", siteName,
			"days_until_expiry", days)
		fmt.Printf("✓ Certificate for %s is valid for %d more days\n", siteName, days)
		return nil
	}

	// 3. 从 API 获取证书
	logger.Info("fetching certificate from API", "site", siteName)
	fmt.Printf("→ Fetching certificate from API...\n")

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	f := fetcher.New(30 * time.Second)
	certData, err := f.Info(ctx, siteConfig.API.URL, siteConfig.API.ReferID)
	if err != nil {
		return fmt.Errorf("failed to fetch certificate: %w", err)
	}

	// 检查证书状态
	if certData.Status != "active" {
		logger.Warn("certificate not active",
			"site", siteName,
			"status", certData.Status)
		fmt.Printf("⚠ Certificate status: %s (not active, will retry later)\n", certData.Status)

		// 更新重试时间
		siteConfig.Metadata.LastCheckAt = time.Now()
		cfgManager.SaveSite(siteConfig)
		return nil
	}

	logger.Info("certificate fetched", "site", siteName, "status", certData.Status)
	fmt.Printf("✓ Certificate fetched successfully\n")

	// 4. 验证证书
	fmt.Printf("→ Validating certificate...\n")

	// 解析证书
	block, _ := pem.Decode([]byte(certData.Cert))
	if block == nil {
		return fmt.Errorf("failed to decode certificate PEM")
	}

	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return fmt.Errorf("failed to parse certificate: %w", err)
	}

	// 验证域名覆盖
	domainValidator := validator.NewDomainValidator(
		siteConfig.Domains,
		siteConfig.Validation.IgnoreDomainMismatch,
	)

	if err := domainValidator.ValidateDomainCoverage(cert); err != nil {
		return fmt.Errorf("domain validation failed: %w", err)
	}

	logger.Info("certificate validated",
		"site", siteName,
		"subject", cert.Subject.CommonName,
		"expires_at", cert.NotAfter)
	fmt.Printf("✓ Certificate validated\n")

	// 5. 备份旧证书
	if siteConfig.Backup.Enabled {
		fmt.Printf("→ Backing up old certificate...\n")

		backupManager := backup.NewManager(
			cfgManager.GetBackupDir(),
			siteConfig.Backup.KeepVersions,
		)

		certInfo := &backup.CertInfo{
			Subject:   cert.Subject.CommonName,
			Serial:    fmt.Sprintf("%X", cert.SerialNumber),
			NotBefore: cert.NotBefore,
			NotAfter:  cert.NotAfter,
		}

		backupPath, err := backupManager.Backup(
			siteName,
			siteConfig.Paths.Certificate,
			siteConfig.Paths.PrivateKey,
			certInfo,
		)

		if err != nil {
			logger.Warn("backup failed", "error", err)
			fmt.Printf("⚠ Backup failed: %v\n", err)
		} else {
			logger.Info("backup created", "path", backupPath)
			fmt.Printf("✓ Backup created: %s\n", backupPath)
		}
	}

	// 6. 部署证书
	fmt.Printf("→ Deploying certificate...\n")

	nginxDeployer := deployer.NewNginxDeployer(
		siteConfig.Paths.Certificate,
		siteConfig.Paths.PrivateKey,
		siteConfig.Reload.TestCommand,
		siteConfig.Reload.ReloadCommand,
	)

	keyContent, err := os.ReadFile(siteConfig.Paths.PrivateKey)
	if err != nil {
		return fmt.Errorf("read private key failed: %w", err)
	}

	if err := nginxDeployer.Deploy(certData.Cert, certData.IntermediateCert, string(keyContent)); err != nil {
		return fmt.Errorf("deployment failed: %w", err)
	}

	logger.Info("certificate deployed", "site", siteName)
	fmt.Printf("✓ Certificate deployed successfully\n")

	// 7. 更新元数据
	siteConfig.Metadata.LastDeployAt = time.Now()
	siteConfig.Metadata.LastCheckAt = time.Now()
	siteConfig.Metadata.CertExpiresAt = cert.NotAfter
	siteConfig.Metadata.CertSerial = fmt.Sprintf("%X", cert.SerialNumber)

	if err := cfgManager.SaveSite(siteConfig); err != nil {
		logger.Warn("failed to update metadata", "error", err)
	}

	fmt.Printf("\n✓ Deployment completed for %s\n", siteName)
	fmt.Printf("  Certificate expires: %s\n", cert.NotAfter.Format("2006-01-02 15:04:05"))
	fmt.Printf("  Days until expiry: %d\n", siteConfig.DaysUntilExpiry())

	return nil
}

// runStatus 执行状态查看
func runStatus(cmd *cobra.Command, args []string) error {
	sites, err := cfgManager.ListSites()
	if err != nil {
		return fmt.Errorf("failed to list sites: %w", err)
	}

	if len(sites) == 0 {
		fmt.Println("No sites configured")
		return nil
	}

	fmt.Printf("Sites: %d\n\n", len(sites))
	fmt.Println("┌────────────────────┬─────────┬─────────────────────┬──────────────┐")
	fmt.Println("│ Site               │ Enabled │ Expires At          │ Days Left    │")
	fmt.Println("├────────────────────┼─────────┼─────────────────────┼──────────────┤")

	for _, site := range sites {
		enabled := "✓"
		if !site.Enabled {
			enabled = "✗"
		}

		expiresAt := "N/A"
		daysLeft := "N/A"
		if !site.Metadata.CertExpiresAt.IsZero() {
			expiresAt = site.Metadata.CertExpiresAt.Format("2006-01-02 15:04")
			days := site.DaysUntilExpiry()
			daysLeft = fmt.Sprintf("%d", days)

			if days <= 7 {
				daysLeft += " ⚠️"
			}
		}

		fmt.Printf("│ %-18s │ %-7s │ %-19s │ %-12s │\n",
			truncate(site.SiteName, 18),
			enabled,
			expiresAt,
			daysLeft,
		)
	}

	fmt.Println("└────────────────────┴─────────┴─────────────────────┴──────────────┘")

	return nil
}

// runCheck 执行检查
func runCheck(cmd *cobra.Command, args []string) error {
	var sites []*config.SiteConfig
	var err error

	if siteName != "" {
		// 检查单个站点
		site, err := cfgManager.LoadSite(siteName)
		if err != nil {
			return fmt.Errorf("failed to load site: %w", err)
		}
		sites = []*config.SiteConfig{site}
	} else {
		// 检查所有站点
		sites, err = cfgManager.ListSites()
		if err != nil {
			return fmt.Errorf("failed to list sites: %w", err)
		}
	}

	needsRenewal := 0
	for _, site := range sites {
		if !site.Enabled {
			continue
		}

		if site.NeedsRenewal() {
			needsRenewal++
			days := site.DaysUntilExpiry()
			fmt.Printf("⚠ %s needs renewal (expires in %d days)\n", site.SiteName, days)
		} else {
			days := site.DaysUntilExpiry()
			fmt.Printf("✓ %s is OK (expires in %d days)\n", site.SiteName, days)
		}
	}

	if needsRenewal > 0 {
		fmt.Printf("\n%d site(s) need renewal\n", needsRenewal)
	} else {
		fmt.Println("\nAll certificates are valid")
	}

	return nil
}

// setupLogger 初始化日志
func setupLogger() *slog.Logger {
    // 从环境变量读取日志级别，默认 INFO
    levelStr := strings.ToUpper(strings.TrimSpace(os.Getenv("LOG_LEVEL")))
    var level slog.Level
    switch levelStr {
    case "DEBUG":
        level = slog.LevelDebug
    case "WARN", "WARNING":
        level = slog.LevelWarn
    case "ERROR":
        level = slog.LevelError
    default:
        level = slog.LevelInfo
    }
    handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: level})
    return slog.New(handler)
}

// runDaemon 执行守护进程
func runDaemon(cmd *cobra.Command, args []string) error {
	logger.Info("starting daemon mode")
	fmt.Println("→ Starting daemon mode...")

	// 创建守护进程
	d := daemon.New(cfgManager, logger)

	// 运行守护进程
	fmt.Println("✓ Daemon started")
	fmt.Println("  Press Ctrl+C to stop")

	return d.Run()
}

// runScan 执行扫描
func runScan(cmd *cobra.Command, args []string) error {
    logger.Info("scanning nginx configuration")
    fmt.Println("→ Scanning Nginx configuration...")

    // 创建扫描器
    var nginxScanner *scanner.NginxScanner
    if strings.TrimSpace(nginxDirs) != "" {
        parts := strings.Split(nginxDirs, ",")
        // 去除空白
        var cleaned []string
        for _, p := range parts {
            s := strings.TrimSpace(p)
            if s != "" {
                cleaned = append(cleaned, s)
            }
        }
        nginxScanner = scanner.NewNginxScannerWithDirs(cleaned)
    } else {
        nginxScanner = scanner.NewNginxScanner()
    }

	// 执行扫描
	sites, err := nginxScanner.Scan()
	if err != nil {
		return fmt.Errorf("failed to scan: %w", err)
	}

	if len(sites) == 0 {
		fmt.Println("✗ No SSL sites found")
		return nil
	}

	logger.Info("scan completed", "sites_found", len(sites))
	fmt.Printf("✓ Found %d SSL site(s)\n\n", len(sites))

	// 显示扫描结果
	fmt.Println("┌────────────────────┬──────────────────────┬──────────────┬────────────────┐")
	fmt.Println("│ Site               │ Cert Path            │ Expires      │ Days Left      │")
	fmt.Println("├────────────────────┼──────────────────────┼──────────────┼────────────────┤")

	for _, site := range sites {
		expiresStr := "N/A"
		daysLeftStr := "N/A"

		if !site.CurrentExpiry.IsZero() {
			expiresStr = site.CurrentExpiry.Format("2006-01-02")
			daysLeft := int(time.Until(site.CurrentExpiry).Hours() / 24)
			daysLeftStr = fmt.Sprintf("%d", daysLeft)

			if daysLeft <= 7 {
				daysLeftStr += " ⚠️"
			}
		}

		fmt.Printf("│ %-18s │ %-20s │ %-12s │ %-14s │\n",
			truncate(site.Name, 18),
			truncate(site.CertPath, 20),
			expiresStr,
			daysLeftStr,
		)

		// 显示域名
		if len(site.Domains) > 1 {
			fmt.Printf("│   Domains: %-62s │\n",
				truncate(strings.Join(site.Domains, ", "), 62))
		}
	}

	fmt.Println("└────────────────────┴──────────────────────┴──────────────┴────────────────┘")

	// 提示下一步操作
	fmt.Println("\nNext steps:")
	fmt.Println("  1. Use 'init' command to create site configurations")
	fmt.Println("  2. Or use 'auto' command to automatically configure and deploy")

	return nil
}

// truncate 截断字符串
func truncate(s string, maxLen int) string {
	if len(s) <= maxLen {
		return s
	}
	return s[:maxLen-3] + "..."
}
